package com.magi.core;

import com.magi.constant.TypeMappingFileNames;
import com.magi.exception.TypeMappingNotFoundException;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.IOException;
import java.util.Iterator;

public class DatabaseTypeMapping implements IDatabaseTypeMapping{
    private final Document config;

    public static class Keywords {
        public static final String JAVA_TYPE = "java-type";
        public static final String DATABASE_TYPE = "database-type";
        public static final String MAPPING = "mapping";
        public static final String PACKAGE = "package";

    }



    public DatabaseTypeMapping(String filename) throws DocumentException {
        this.config = new SAXReader().read(ClassLoader.getSystemResourceAsStream(filename));
    }

    public DatabaseTypeMapping() throws DocumentException {
        try {
            this.config = new SAXReader().read(AppUtils.getResourceFileStream(TypeMappingFileNames.MYSQL));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private Element getRoot() {
        return this.config.getRootElement();
    }

    private void attributeNotFound(String attrName, String elementName) {
        throw new RuntimeException("mapping file error, attribute '" + attrName + "' not found in element '" + elementName + "'");
    }

    private Element searchMappingFormRootByJavaType(String javaType) {
        Iterator<Element> iterator= this.getRoot().elementIterator(Keywords.MAPPING);
        while (iterator.hasNext()) {
            Element e = iterator.next();
            if (e.attributeValue(Keywords.JAVA_TYPE) == null) { this.attributeNotFound(Keywords.JAVA_TYPE, e.getName()); }
            if (e.attributeValue(Keywords.JAVA_TYPE).trim().equals(javaType)) {
                return e;
            }
        }
        return null;
    }

    private Element searchDatabaseTypeFormMappingByDatabaseType(Element mapping, String databaseType) {
        Iterator<Element> iterator = mapping.elementIterator(Keywords.DATABASE_TYPE);
        while (iterator.hasNext()) {
            Element e = iterator.next();
            if (e.getTextTrim().equals(databaseType)) {
                return e;
            }
        }
        return null;
    }

    private Element searchMappingFormRootByDatabaseType(String databaseType) {
        Iterator<Element> iterator = this.getRoot().elementIterator(Keywords.MAPPING);
        while (iterator.hasNext()) {
            Element mapping = iterator.next();
            if (this.searchDatabaseTypeFormMappingByDatabaseType(mapping, databaseType) != null) {
                return mapping;
            }
        }
        return null;
    }

    @Override
    public String getJavaType(String columnType) throws TypeMappingNotFoundException {
        Element e = this.searchMappingFormRootByDatabaseType(columnType);
        if (e == null) { throw new TypeMappingNotFoundException("Mapping for column type '"+ columnType +"' not found."); }
        return e.attributeValue(Keywords.JAVA_TYPE);
    }

    @Override
    public String getJavaTypePackage(String javaType) {
        Element e = this.searchMappingFormRootByJavaType(javaType);
        if (e == null) { return null; }
        return e.attributeValue(Keywords.PACKAGE);
    }

    public static void main(String[] args) throws DocumentException, TypeMappingNotFoundException {
        IDatabaseTypeMapping mapping = new DatabaseTypeMapping();
        System.out.println(mapping.getJavaTypePackage("String"));
        System.out.println(mapping.getJavaTypePackage("Date"));
        System.out.println(mapping.getJavaType("BIT"));

    }
}
